package models

import (
	"fmt"
	"strings"

	"gitee.com/sansaniot/ssiot-core/logger"
	"gitee.com/sansaniot/ssiot-core/sdk/model"
	"gorm.io/datatypes"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models/query"
)

type SysDevSchema struct {
	Id           string          `json:"id" gorm:"primaryKey;type:varchar(255);comment:id"`
	DevSn        string          `json:"devSn" gorm:"type:varchar(255);comment:设备序列号"`
	Schema       datatypes.JSON  `json:"schema" gorm:"type:jsonb;comment:schema模板"`
	ProductModel string          `json:"productModel" gorm:"type:varchar(255);comment:产品编码"`
	DevProductId string          `json:"devProductId" gorm:"type:varchar(255);comment:产品Id"`
	CreateTime   *cDto.LocalTime `json:"createTime" gorm:"type:timestamptz(6);comment:创建时间"`
	UpdateTime   *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:更新时间"`

	model.Model
}

func (SysDevSchema) TableName() string {
	return "sys_dev_schema"
}

func (e *SysDevSchema) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysDevSchema) GetId() interface{} {
	return e.Id
}

func (e *SysDevSchema) FindDevSchema(req *query.SysDevSchemaQuery) (err error, one SysDevSchema) {
	err = e.FindOne(&one, cDto.MakeCondition(req.GetNeedSearch()))
	// 产品schema基本信息
	if len(one.ProductModel) > 0 {
		tmpSchemaJson := utils.BytesToMap(one.Schema)
		productSchemaBasic := utils.JsonStrToMap(constant.ProductBasicSchemaTemplate)
		tmpSchemaJson["properties"].(map[string]interface{})["props"].(map[string]interface{})["properties"].(map[string]interface{})["basic"] = productSchemaBasic
		one.Schema = utils.MapToBytes(tmpSchemaJson)
	}
	// 路径处理
	if err == nil && len(req.Path) > 0 {
		paths := strings.Split(req.Path, ".")
		schemaJson := utils.BytesToMap(one.Schema)
		value := utils.GetFieldFromJson(paths, schemaJson)
		one.Schema = utils.DataToJsonByte(value)
	}
	return
}

// 生成config的schema，并与已有合并
func (e *SysDevSchema) InferConfigSchemaMerge(devSn string, configMap map[string]interface{}, updatePath ...string) {
	configSchema := utils.GetObjectSchema(configMap)
	if configSchema["properties"] == nil {
		return
	}
	configSchema = configSchema["properties"].(map[string]interface{})
	if err := e.FindOne(e, cDto.MakeCondition(query.SysDevSchemaQuery{Sn: devSn})); err != nil {
		logger.Error(err)
		return
	}
	tmpSchema := utils.BytesToMap(e.Schema)
	path := []string{"properties", "config", "properties"}
	if len(updatePath) > 0 && len(updatePath[0]) > 0 {
		path = append(path, strings.Split(updatePath[0], ".")...)
	}
	tmpSchema = utils.GetFieldFromJson(path, tmpSchema).(map[string]interface{})
	// 各自拉平层级
	flattenConfigSchema := map[string]interface{}{}
	flattenTmpSchema := map[string]interface{}{}
	utils.FlattenJson(configSchema, flattenConfigSchema, []string{})
	utils.FlattenJson(tmpSchema, flattenTmpSchema, []string{})
	// 已有的key不更新覆盖
	existKeys := make([]string, 0)
	for k, _ := range flattenTmpSchema {
		existKeys = append(existKeys, k)
	}
	updateFlattenMap := map[string]interface{}{}
	for k, v := range flattenConfigSchema {
		if !utils.StrIn(k, existKeys) {
			updateFlattenMap[k] = v
		}
	}
	// 还原拉平层级
	toUpdateMap := utils.FixJsonKeyDot(updateFlattenMap)
	if len(toUpdateMap) == 0 {
		return
	}
	// 更新到schema的config
	toBytes := utils.MapToBytes(toUpdateMap)
	paths := "{properties,config,properties}"
	dataStr := string(toBytes)
	sql := "update sys_dev_schema set schema = jsonb_set(schema::jsonb, '" + paths + "', jsonb_update(schema #> '" + paths + "', '" + dataStr + "' :: jsonb ) ) where dev_sn = '%s'"
	// 防止字符串中存在?占位符
	sql = fmt.Sprintf(sql, devSn)
	if err := e.Orm.Raw(sql).Scan(e).Error; err != nil {
		logger.Error(err)
	}
}
